import time
import pytz
import datetime
import pandas as pd
import math
import calendar
from dateutil.relativedelta import relativedelta

"""
1 --> 带int后缀的，表示中间不带-的datetime格式；
2 --> 带str后缀的，表示中间带-的字符串格式；
3 --> date字样仅表示日期，time字样仅表示时间，同时则表示日期时间一起；
4 --> date_cal计算日期与天数之间关系，输入三种格式不限，输出可以选择（fmt参数）三种格式：
        - int 整形 如 19900101
        - str 字符串 如1990-01-01
        - dt datetime格式 如 1990-01-01(datetime)
"""


class Time:
    __TIME_ZONE = 'Asia/Shanghai'
    __test_db = "_test_"
    __time_coll = "elapse"

    @classmethod
    def current_datetime(cls):
        """返回北京时间"""
        return datetime.datetime.fromtimestamp(int(time.time()), pytz.timezone(cls.__TIME_ZONE))

    @classmethod
    def current_datetime_int(cls):
        """返回当前时区的日期时间整形"""
        return int(cls.current_datetime().strftime("%Y%m%d%H%M%S"))

    @classmethod
    def current_datetime_str(cls):
        """返回当前时区的日期时间整形"""
        return cls.current_datetime().strftime("%Y-%m-%d %H:%M:%S")

    @classmethod
    def current_date(cls):
        """返回当前日期（datetime格式）"""
        return cls.current_datetime().date()

    @classmethod
    def current_date_int(cls):
        """返回当前日期（int格式）"""
        return int(str(cls.current_datetime().date()).replace("-", ""))

    @classmethod
    def current_date_int_str(cls):
        return str(cls.current_datetime().date()).replace("-", "")

    @classmethod
    def current_date_str(cls):
        """返回当前日期（str格式）"""
        return cls.current_datetime().date().strftime("%Y-%m-%d")

    @classmethod
    def current_time(cls):
        """返回当前时间（datetime格式）"""
        return cls.current_datetime().time()

    @classmethod
    def current_time_int(cls):
        """返回当前时间（int格式）"""
        return int(str(cls.current_time()).replace(":", ""))

    @classmethod
    def current_time_str(cls):
        """返回当前时间（str格式）"""
        return cls.current_time().strftime("%H:%M:%S")

    @classmethod
    def date_cal(cls, date: str, days, fmt):
        """返回N天过后的一个日期，注意此时输入的必须是一个整形时间"""
        # 如果data输入为None，则默认为今天的日期
        if date is None:
            res = cls.current_date() + datetime.timedelta(days=days)
        else:
            # 通用转换： 将dt和int格式最后统一转成str格式
            date = str(date).replace("-", "")
            date = datetime.date(year=int(date[0:4]), month=int(date[4:6]), day=int(date[6:8]))
            res = date + datetime.timedelta(days=days)
        if fmt == "dt":
            return res
        elif fmt == "str":
            return str(res)
        elif fmt == "int":
            return int(str(res).replace("-", ""))
        elif fmt == 'int_str':  # 20230330
            return str(res).replace("-", "")

    @classmethod
    def time_cal(cls):
        pass

    @classmethod
    def diff_total_seconds(cls, delt_time):

        return delt_time.total_seconds()

    @classmethod
    def split_time_ranges(cls, from_time, to_time, frequency):
        from_time, to_time = pd.to_datetime(from_time), pd.to_datetime(to_time)
        time_range = list(pd.date_range(from_time, to_time, freq='%sS' % frequency))
        if to_time not in time_range:
            time_range.append(to_time)
        time_range = [item.strftime("%Y-%m-%d %H:%M:%S") for item in time_range]
        time_ranges = []
        for item in time_range:
            f_time = item
            t_time = (datetime.datetime.strptime(item, "%Y-%m-%d %H:%M:%S") + datetime.timedelta(seconds=frequency))
            if t_time >= to_time:
                t_time = to_time.strftime("%Y-%m-%d %H:%M:%S")
                time_ranges.append([f_time, t_time])
                break
            time_ranges.append([f_time, t_time.strftime("%Y-%m-%d %H:%M:%S")])
        return time_ranges

    @classmethod
    def eq_diff(cls, start_date=None, end_date=None):
        """将时间分为等长的一个个区间"""
        pass

    @classmethod
    def time_split(cls, from_date="1991-01-01", to_date="1991-01-31", count=4):

        """
        1、将开始和结束时间之间的间隔天数算出来；
        2、将日期通过给定的进程参数分成片；
        3、根据已知数据，给日期分片结果。
        """
        """1、算天数"""
        days_delt = cls.date_delta(from_date=from_date, to_date=to_date)

        """2、给定进程，计算分片-向上取整"""
        per_piese = math.floor(days_delt / count)

        """3、分片结果"""
        start_date = from_date
        result = []
        for i in range(count):
            if i != count - 1:
                end_date = Time.date_cal(start_date, per_piese)
            else:
                end_date = to_date
            result.append({"start_date": start_date, "end_date": end_date})
            start_date = Time.date_cal(end_date, 1)
        return result

    @classmethod
    def date_delta(cls, from_date, to_date):
        """将标准连字号格式日期相减，计算两者之间的差值"""
        from_date = str(from_date).replace('-', '')
        to_date = str(to_date).replace('-', '')
        dt1 = time.strptime(from_date, "%Y%m%d")
        dt2 = time.strptime(to_date, "%Y%m%d")

        dt_1 = datetime.datetime(dt1[0], dt1[1], dt1[2])
        dt_2 = datetime.datetime(dt2[0], dt2[1], dt2[2])
        date_delta = dt_2 - dt_1
        return date_delta.days

    @classmethod
    def current_year_first_day(cls):
        """本年度第一天，类型默认为str"""
        return f"{cls.current_date_str()[0:4]}0101"

    @classmethod
    def current_year_last_day(cls):
        """本年度最后一天，类型默认为str"""
        return f"{cls.current_date_str()[0:4]}1231"

    @classmethod
    def current_month_last_day(cls):
        """本月的最后一天"""
        now = datetime.date.today()
        return datetime.datetime(now.year, now.month, calendar.monthrange(now.year, now.month)[1]).strftime("%Y%m%d")

    @classmethod
    def month_last_day(cls, date):
        if date is None:
            date = cls.current_date()
        else:
            # 通用转换： 将dt和int格式最后统一转成str格式
            date = str(date).replace("-", "")
            date = datetime.date(year=int(date[0:4]), month=int(date[4:6]), day=int(date[6:8]))
        return datetime.datetime(date.year, date.month, calendar.monthrange(date.year, date.month)[1]).strftime(
            "%Y%m%d")

    @classmethod
    def current_month_first_day(cls):
        """本月第一天"""
        return f"{cls.current_date_str().replace('-', '')[0:6]}01"

    @classmethod
    def sleep(cls, second=3):
        time.sleep(second)

    @classmethod
    def to_timestamp(cls, dateTimeStr="2021-10-14 15:43:00"):

        """
        字符串转换成时间戳
        """
        return datetime.datetime.strptime(dateTimeStr, '%Y-%m-%d %H:%M:%S').timestamp()

    @classmethod
    def day_cal(cls, date, days, fmt="%Y%m%d"):
        time_tuple = time.strptime(date, fmt)
        year, month, day = time_tuple[:3]
        a_date = datetime.date(year, month, day)
        cal_date = a_date + datetime.timedelta(days=days)
        ret = cal_date.strftime(fmt)
        return ret

    @classmethod
    def min_cal(cls, datetime_, mins, fmt="%Y-%m-%d %H:%M:%S"):
        dd = datetime.datetime.strptime(datetime_, fmt)
        if mins > 0:
            cal_time = dd + datetime.timedelta(minutes=abs(mins))
        else:
            cal_time = dd - datetime.timedelta(minutes=abs(mins))
        ret = cal_time.strftime(fmt)
        return ret

    @classmethod
    def str2time(cls, str_time, fmt="%Y-%m-%d %H:%M:%S"):
        return datetime.datetime.strptime(str_time, fmt)

    @classmethod
    def time2str(cls, dt, fmt="%Y-%m-%d %H:%M:%S"):
        return dt.strftime(fmt)

    @classmethod
    def is_monday_today(cls):
        return True if cls.current_datetime().isocalendar()[2] == 1 else False

    @classmethod
    def is_first_day_this_month(cls):
        return True if int(Time.time2str(Time.current_datetime(), fmt="%d")) == 1 else False

    @classmethod
    def monday(cls):
        now = Time.current_datetime()
        diff = now.isocalendar()[2] - 1
        monday = Time.day_cal(Time.time2str(now), -diff, "%Y-%m-%d %H:%M:%S")
        monday = int(Time.time2str(Time.str2time(monday), fmt="%Y%m%d%H%M"))
        return monday

    @classmethod
    def prev_friday(cls):
        today = datetime.datetime.today()
        num = today.weekday()
        if num == 4:
            return int(str(today).replace("-", "")[:8])
        num = (num + 3) % 7
        if num == 0:
            num = 7
        return int(str(today - datetime.timedelta(days=num)).replace("-", "")[:8])

    @classmethod
    def is_in_the_same_period(cls, first_datetime_str: str, second_datetime_str: str, period_type: str) -> bool:
        """
        指定两个日期判断是否在同一个周期
        :param first_datetime_str 第一个日期 格式yyyymmddHHMMSS
        :param second_datetime_str 第二个日期，格式yyyymmddHHMMSS
        :param period_type 周期类型，包括周(weekly)、月(monthly)、年(yearly)
        """
        date_first = datetime.datetime.strptime(first_datetime_str, '%Y%m%d%H%M%S')
        date_second = datetime.datetime.strptime(second_datetime_str, '%Y%m%d%H%M%S')
        if period_type == 'weekly':
            return date_first.isocalendar()[0] == date_second.isocalendar()[0] and date_first.isocalendar()[1] == \
                   date_second.isocalendar()[1]
        elif period_type == 'monthly':
            return date_first.year == date_second.year and date_first.month == date_second.month
        elif period_type == 'yearly':
            return date_first.isocalendar()[0] == date_second.isocalendar()[0]
        else:
            raise ValueError('周期类型只能输入：weekly/monthly/yearly')

    @classmethod
    def after_day_date(cls, days: int, fmt: str = "date"):
        """
        计算指定日期后的日期
        :param days 天数
        :param fmt 日期格式，str或date
        """
        if fmt == 'str':
            return str(datetime.datetime.now().date() + datetime.timedelta(days=days))
        elif fmt == 'date':
            return datetime.datetime.now().date() + datetime.timedelta(days=days)

    @classmethod
    def after_day_datetime(cls, days: int, fmt: str = "datetime"):
        """
        计算指定日期后的日期
        :param days 天数
        :param fmt 日期格式，str或datetime
        """
        if fmt == 'str':
            return str(datetime.datetime.now().replace(microsecond=days) + datetime.timedelta(days=days))
        elif fmt == 'date':
            return datetime.datetime.now().replace(microsecond=days) + datetime.timedelta(days=days)

    @classmethod
    def after_month_date(cls, months: int, fmt: str = "date"):
        """
        从当前时间开始，计算指定月份个数后的日期
        例如：3个月后的日期
        计算每个月有多少天
        :param months 月份个数
        :param fmt 日期格式，str或date
        """
        if fmt == 'date':
            return datetime.datetime.now().date() + relativedelta(months=months)
        elif fmt == 'str':
            return str(datetime.datetime.now().date() + relativedelta(months=months))

    @classmethod
    def after_month_datetime(cls, months: int, fmt: str = "datetime"):
        """
        从当前时间开始，计算指定月份个数后的日期
        例如：3个月后的日期
        计算每个月有多少天
        :param months 月份个数
        :param fmt 日期格式，str或datetime
        """
        if fmt == 'datetime':
            return datetime.datetime.now().replace(microsecond=0) + relativedelta(months=months)
        elif fmt == 'str':
            return str(datetime.datetime.now().replace(microsecond=0) + relativedelta(months=months))


if __name__ == '__main__':
    # print(Time.current_date_int_str())
    print(Time.date_cal(date='20230330', days=-100, fmt='int_str'))
